#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
using namespace std;

const int basesize = 1024;
const int gnum = 64;//环境变量表和命令行参数表的大小
char* genv[gnum];
char* gargv[gnum];
char buff[basesize];//辅助数组，存储输入的命令
char pwd[basesize];
char pwdenv[basesize];
int gargc = 0;
int lastcode = 0;

#define NonRedir 0
#define InputRedir 1
#define OutputRedir 2
#define AppendRedir 3

int redir = NonRedir;
char* filename = nullptr;

#define SkipSpace(pos) do{\
    while(isspace(*pos)){\
        pos++;\
    }\
}while(0)\


string getUsername()
{
    string username = getenv("USER");
    return username == "" ? "None" : username;
}
string getHostname()
{
    char hostname[20];
    int n = gethostname(hostname,sizeof(hostname));
    if(n < 0)
    {
        perror("gethostname");
        exit(1);
    }
    return hostname;
}
string GetPwd()
{
    if(getcwd(pwd,sizeof(pwd)) == nullptr) 
    {
        return "Node";
    }
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    for(int i = 0;genv[i];i++)
    {
        string str = genv[i];
        char* before = (char*)str.substr(0,3).c_str();
        if(strcmp(before,"PWD") == 0)
        {
            strncpy(genv[i],pwdenv,strlen(pwdenv));
            genv[i][strlen(pwdenv)] = 0;
            return pwd;
        }
    }

}

string Lastdir()
{
    string cur = GetPwd();
    if(cur == "/" || cur == "None")
        return cur;
    size_t pos = cur.rfind('/');
    if(pos == std::string::npos) return cur;
    return cur.substr(pos + 1);
}

void PrintCommand()
{
    string username = getUsername();
    string hostname = getHostname();
    string pwd  = Lastdir();
    if(username != "None" && hostname != "None" && pwd != "None")
    {
        cout << username << "@" << hostname << ":" << pwd << "$";
        fflush(stdout);
    }
    else 
    exit(1);
}
bool GetCommand()
{
    memset(buff,0,sizeof buff);
    char* result = fgets(buff,basesize,stdin);
    if(result == nullptr)
    {
        return false;
    }
    //cout << result << endl;
    buff[strlen(buff) - 1] = 0;
    if(strlen(buff) == 0) return false;
    return true;
}
void ResetGval()
{
    gargc = 0;
    filename = nullptr;
    redir = NonRedir;
}
void JudgeRedir()
{
    int end = strlen(buff) - 1;
    while(end >= 0)
    {
        if(buff[end] == '>')
        {
            redir = OutputRedir;
            buff[end] = 0;
            filename = &buff[end + 1];
            SkipSpace(filename);
            break;
        }
        else if(buff[end] == '>' && end > 0 && buff[end - 1] == '>')
        {
            redir = AppendRedir;
            buff[end] = 0;
            filename = &buff[end + 1];
            SkipSpace(filename);
            break;
        }
        else if(buff[end] == '<')
        {
            redir = InputRedir;
            buff[end] = 0;
            filename = &buff[end + 1];
            SkipSpace(filename);
            break;
        }
        end--;
    }
    //printf("filename:%s\n",filename);
   // printf("redir:%d\n",redir);

}
    
void Parse()
{
    const char* sep = " ";
    gargv[gargc++] = strtok(buff,sep);
    while((bool)(gargv[gargc++] = strtok(nullptr,sep)));
    //cout << gargc << endl;
    gargv[gargc] = nullptr;
    gargc--;
}
void ParseCommand()
{
    ResetGval();
    JudgeRedir();
    Parse();
}
void Redir()
{
    if(redir == InputRedir)
    {
        if(filename)
        {
            int fp = open(filename,O_RDONLY);
            if(fp > 0)
            {
                dup2(fp,0);
            }
            else 
            {
                perror("open");
                exit(4);
            }
        }
        else 
        {
            exit(1);
        }
    }
    else if(redir == OutputRedir)
    {
        if(filename)
        {
            int fp = open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
            if(fp > 0)
            dup2(fp,1);
            else
            {
                perror("open");
                exit(4);
            }
        }
        else 
        {
            exit(2);
        }
    }
    else if(redir == AppendRedir)
    {
        if(filename)
        {
            int fp = open(filename,O_WRONLY | O_CREAT | O_APPEND,0666);
            if(fp > 0)
            dup2(fp,1);
            else 
            {
                perror("open");
                exit(4);
            }
        }
        else
        {
            exit(3);
        }
    }
}
bool ExecuteCommand()
{
    pid_t id = fork();
    if(id < 0) return false;
    if(id == 0)
    {
        Redir();
        //子进程
        // 1. 执行命令
        //cout << "gragv[0] = " << gargv[0] << endl;
        int ret = execvpe(gargv[0], gargv, genv);
        //char* argv[] = {
        //    "ls",
        //    "-l",
        //    "-a",
        //    nullptr
        // };
        //int ret = execvpe("ls",gargv,genv);
        cout << errno << endl;
        lastcode  = 1;
        // 2. 退出
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        lastcode = WEXITSTATUS(status);
        return true;
    }
    lastcode = 100;
    return false;
    //pid_t id = fork();
    //if(id == 0)
    //{
    //    cout << gargv[0] << endl;
    //    int ret = execvpe(gargv[0],gargv,genv);
    //    if(ret == -1)
    //    {
    //        cout << errno << endl;
    //        return false;
    //    }
    //    return true;
    //}
    //int status;
    //pid_t wid = waitpid(id,&status,0);
    //if(wid > 0)
    //{
    //    cout << "等待子进程成功" << endl;
    //    return true;
    //}
    //return false;

}
void AddEnv()
{
    int index = 0;
    while(genv[index])
    {
        index++;
    }
    genv[index] = (char*)malloc(strlen(gargv[1] + 1));
    strncpy(genv[index],gargv[1],strlen(gargv[1]) + 1);
    genv[++index] = nullptr;

}
bool CheckCommand()
{
    if(strcmp(gargv[0],"cd") == 0)
    {
        if(gargc == 2)
        {
            chdir(gargv[1]);
            GetPwd();
            lastcode = 0;
        }
        else
        {
            lastcode = 2;
        }
        return true;
    }
    else if(strcmp(gargv[0],"echo") == 0)
    {
        if(gargc == 2)
        {
            if(gargv[1][0] == '$')
            {
                if(gargv[1][1] == '?')
                { 
                    printf("%d\n",lastcode);
                    lastcode = 0;
                }
            }
            else 
            {
                printf("%s\n",gargv[1]);
                lastcode = 0;
            }
        }
        else 
        lastcode = 3;
        return true;
    }
    else if(strcmp(gargv[0],"env") == 0)
    {
        for(int i = 0;genv[i];i++)
        cout << genv[i] << endl;
        lastcode = 0;
        return true;
    }
    else if(strcmp(gargv[0],"export") == 0)
    {
        if(gargc == 2)
        {
            AddEnv();
            lastcode = 0;
            return true;
        }
        else 
        lastcode = 4;
        return true;
    }

    return false;

}

void debug()
{
    for(int i = 0;genv[i];i++)
    {
        cout << genv[i] << endl;
    }
    cout << "//////////////" << endl;
    for(int i = 0;gargv[i];i++)
    {
        cout << gargv[i] << endl;
    }
}
void Initenv()
{
    extern char** environ;
    int index = 0;
    while(environ[index])
    {
        genv[index] = (char*)malloc(strlen(environ[index] + 1));
        strncpy(genv[index],environ[index],strlen(environ[index]));
        index++;
    }
    genv[index] = nullptr;
}
int main()
{
    Initenv();
    while(1)
    {
        PrintCommand();//打印命令提示符
        //sleep(10);
        bool ret = GetCommand();//从标准输入获取命令
        //cout << "获取命令成功" << endl;
        if(ret = true)
            ParseCommand();//分析命令
        else 
            continue;
       // cout << endl;
       //debug();
       if(CheckCommand())
       {
           continue;
       }

       ExecuteCommand();//处理命令
    }


    return 0;
}
